Maximum Sharpe Ratio Portfolio#
import numpy as np
from plotly.io import show
from sklearn.model_selection import train_test_split
from skfolio import Population, RiskMeasure
from skfolio.datasets import load_sp500_dataset
from skfolio.optimization import InverseVolatility, MeanRisk, ObjectiveFunction
from skfolio.preprocessing import prices_to_returns
Data Preparation#
price = load_sp500_dataset()
price.head()
| AAPL | AMD | BAC | BBY | CVX | GE | HD | JNJ | JPM | KO | LLY | MRK | MSFT | PEP | PFE | PG | RRC | UNH | WMT | XOM | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Date | ||||||||||||||||||||
| 1990-01-02 | 0.264 | 4.125 | 4.599 | 0.144 | 4.991 | 14.391 | 1.117 | 3.438 | 3.394 | 2.235 | 6.658 | 4.215 | 0.384 | 4.738 | 1.021 | 3.860 | 3.322 | 0.310 | 3.653 | 4.068 |
| 1990-01-03 | 0.266 | 4.000 | 4.636 | 0.161 | 4.910 | 14.364 | 1.121 | 3.452 | 3.508 | 2.203 | 6.658 | 4.282 | 0.386 | 4.692 | 1.024 | 3.853 | 3.322 | 0.304 | 3.653 | 4.027 |
| 1990-01-04 | 0.267 | 3.938 | 4.537 | 0.159 | 4.847 | 14.283 | 1.128 | 3.459 | 3.522 | 2.192 | 6.621 | 4.215 | 0.397 | 4.646 | 1.041 | 3.777 | 3.322 | 0.301 | 3.634 | 3.987 |
| 1990-01-05 | 0.268 | 3.812 | 4.438 | 0.159 | 4.775 | 14.148 | 1.113 | 3.423 | 3.536 | 2.174 | 6.549 | 4.128 | 0.387 | 4.581 | 1.032 | 3.709 | 3.322 | 0.288 | 3.595 | 3.966 |
| 1990-01-08 | 0.269 | 3.812 | 4.463 | 0.147 | 4.820 | 14.229 | 1.102 | 3.481 | 3.536 | 2.220 | 6.549 | 4.181 | 0.393 | 4.664 | 1.023 | 3.777 | 3.322 | 0.282 | 3.644 | 4.027 |
asset_linear_returns = prices_to_returns(price)
asset_linear_returns.head()
| AAPL | AMD | BAC | BBY | CVX | GE | HD | JNJ | JPM | KO | LLY | MRK | MSFT | PEP | PFE | PG | RRC | UNH | WMT | XOM | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Date | ||||||||||||||||||||
| 1990-01-03 | 0.007576 | -0.030303 | 0.008045 | 0.118056 | -0.016229 | -0.001876 | 0.003581 | 0.004072 | 0.033589 | -0.014318 | 0.000000 | 0.015896 | 0.005208 | -0.009709 | 0.002938 | -0.001813 | 0.0 | -0.019355 | 0.000000 | -0.010079 |
| 1990-01-04 | 0.003759 | -0.015500 | -0.021355 | -0.012422 | -0.012831 | -0.005639 | 0.006244 | 0.002028 | 0.003991 | -0.004993 | -0.005557 | -0.015647 | 0.028497 | -0.009804 | 0.016602 | -0.019725 | 0.0 | -0.009868 | -0.005201 | -0.009933 |
| 1990-01-05 | 0.003745 | -0.031996 | -0.021821 | 0.000000 | -0.014855 | -0.009452 | -0.013298 | -0.010408 | 0.003975 | -0.008212 | -0.010874 | -0.020641 | -0.025189 | -0.013991 | -0.008646 | -0.018004 | 0.0 | -0.043189 | -0.010732 | -0.005267 |
| 1990-01-08 | 0.003731 | 0.000000 | 0.005633 | -0.075472 | 0.009424 | 0.005725 | -0.009883 | 0.016944 | 0.000000 | 0.021159 | 0.000000 | 0.012839 | 0.015504 | 0.018118 | -0.008721 | 0.018334 | 0.0 | -0.020833 | 0.013630 | 0.015381 |
| 1990-01-09 | -0.007435 | 0.016527 | 0.000000 | 0.000000 | -0.007469 | -0.020803 | -0.026316 | -0.031026 | -0.031957 | -0.007658 | -0.011147 | -0.007893 | -0.002545 | -0.013722 | -0.021505 | 0.000000 | 0.0 | -0.024823 | -0.026619 | -0.020114 |
X_train, X_test = train_test_split(asset_linear_returns, shuffle=False)
X_test.head()
| AAPL | AMD | BAC | BBY | CVX | GE | HD | JNJ | JPM | KO | LLY | MRK | MSFT | PEP | PFE | PG | RRC | UNH | WMT | XOM | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Date | ||||||||||||||||||||
| 2014-09-29 | -0.006353 | -0.013889 | -0.001183 | 0.021517 | -0.007574 | -0.008196 | 0.000421 | -0.005226 | -0.003803 | 0.001190 | -0.002136 | 0.000347 | 0.000649 | 0.000207 | 0.001677 | -0.001653 | 0.014331 | -0.001042 | -0.005356 | -0.010485 |
| 2014-09-30 | 0.006394 | -0.039437 | 0.002369 | -0.001782 | -0.010200 | 0.007874 | -0.012268 | 0.000464 | -0.001493 | 0.009698 | -0.007512 | -0.002199 | -0.001721 | -0.000635 | -0.006698 | -0.008292 | -0.012518 | -0.002997 | 0.005132 | -0.004013 |
| 2014-10-01 | -0.015594 | -0.017595 | -0.013486 | -0.028875 | -0.013996 | -0.017960 | -0.007849 | -0.021480 | -0.007796 | 0.001859 | 0.003552 | -0.009603 | -0.009920 | -0.004724 | -0.014230 | -0.007156 | -0.005323 | -0.015893 | -0.004585 | -0.012653 |
| 2014-10-02 | 0.007266 | 0.011940 | 0.003594 | 0.003063 | -0.004596 | -0.001590 | 0.013405 | -0.004310 | -0.008934 | -0.001856 | -0.003836 | 0.002389 | -0.003054 | -0.000763 | -0.003068 | -0.001090 | 0.007563 | 0.000242 | 0.001441 | 0.004734 |
| 2014-10-03 | -0.002778 | 0.002950 | 0.024294 | 0.019871 | 0.005126 | 0.011145 | 0.014090 | 0.012316 | 0.024827 | 0.007963 | 0.012947 | 0.017827 | 0.007215 | 0.009944 | 0.005499 | 0.008914 | -0.017500 | 0.013185 | 0.014306 | 0.006647 |
Model#
Create a Maximize Sharpe Ratio model and fit it on training set.
max_sharpe_model = MeanRisk(
risk_measure=RiskMeasure.STANDARD_DEVIATION,
objective_function=ObjectiveFunction.MAXIMIZE_RATIO,
portfolio_params=dict(name="Max Sharpe Ratio"),
)
max_sharpe_model.fit(X_train)
max_sharpe_model.weights_
array([9.12639732e-02, 2.85049328e-08, 2.31977012e-08, 1.04246239e-01,
3.08020521e-02, 3.60047992e-08, 4.18252122e-02, 1.72210429e-01,
4.01080513e-08, 7.82855158e-03, 8.14858866e-08, 8.52061207e-08,
1.12788995e-01, 7.13632483e-02, 2.64892622e-07, 1.10516792e-01,
7.81272384e-02, 1.75517534e-01, 2.39852902e-07, 3.50893616e-03])
benchmark_model = InverseVolatility(
portfolio_params=dict(name="Inverse Volatility")
)
benchmark_model.fit(X_train)
benchmark_model.weights_
array([0.03309451, 0.02507122, 0.03536272, 0.02895846, 0.06352572,
0.05434978, 0.04758711, 0.07073444, 0.03891936, 0.06692204,
0.05564459, 0.05576718, 0.04704164, 0.0638535 , 0.05597692,
0.06760446, 0.02585011, 0.03990667, 0.0577429 , 0.06608667])
Prediction#
The predict method returns a portfolio object.
pred_max_sharpe = max_sharpe_model.predict(X_test)
pred_benchmark = benchmark_model.predict(X_test)
pred_max_sharpe
<Portfolio Max Sharpe Ratio>
pred_benchmark
<Portfolio Inverse Volatility>
Print some properties.
print(pred_max_sharpe.returns)
print(pred_benchmark.returns)
print(pred_max_sharpe.annualized_sharpe_ratio)
print(pred_benchmark.annualized_sharpe_ratio)
[ 0.00136218 -0.00294776 -0.01437361 ... 0.0082964 0.00104224
-0.01662141]
[-0.00206867 -0.00293476 -0.01046662 ... 0.0086359 0.00202183
-0.0113165 ]
0.9684677102652403
0.8930043602505584
pred_max_sharpe.plot_cumulative_returns()
pred_benchmark.plot_cumulative_returns()
pred_max_sharpe.summary()
Mean 0.072%
Annualized Mean 18.14%
Variance 0.014%
Annualized Variance 3.51%
Semi-Variance 0.0069%
Annualized Semi-Variance 1.75%
Standard Deviation 1.18%
Annualized Standard Deviation 18.73%
Semi-Deviation 0.83%
Annualized Semi-Deviation 13.22%
Mean Absolute Deviation 0.76%
CVaR at 95% 2.70%
EVaR at 95% 5.42%
Worst Realization 10.41%
CDaR at 95% 14.09%
MAX Drawdown 31.85%
Average Drawdown 2.98%
EDaR at 95% 19.42%
First Lower Partial Moment 0.38%
Ulcer Index 0.048
Gini Mean Difference 1.15%
Value at Risk at 95% 1.66%
Drawdown at Risk at 95% 10.33%
Entropic Risk Measure at 95% 3.00
Fourth Central Moment 0.000038%
Fourth Lower Partial Moment 0.000014%
Skew 35.24%
Kurtosis 1942.93%
Sharpe Ratio 0.061
Annualized Sharpe Ratio 0.97
Sortino Ratio 0.086
Annualized Sortino Ratio 1.37
Mean Absolute Deviation Ratio 0.095
First Lower Partial Moment Ratio 0.19
Value at Risk Ratio at 95% 0.043
CVaR Ratio at 95% 0.027
Entropic Risk Measure Ratio at 95% 0.00024
EVaR Ratio at 95% 0.013
Worst Realization Ratio 0.0069
Drawdown at Risk Ratio at 95% 0.0070
CDaR Ratio at 95% 0.0051
Calmar Ratio 0.0023
Average Drawdown Ratio 0.024
EDaR Ratio at 95% 0.0037
Ulcer Index Ratio 0.015
Gini Mean Difference Ratio 0.063
Effective Number of Assets 8.434345967826763
Assets Number 20
dtype: object
Analysis#
For holistic analysis, load both portfolio into a Population class object.
population = Population([pred_max_sharpe, pred_benchmark])
population.plot_composition()
population.plot_cumulative_returns()
Full evaluation summary.
population.summary()
| Max Sharpe Ratio | Inverse Volatility | |
|---|---|---|
| Mean | 0.072% | 0.062% |
| Annualized Mean | 18.14% | 15.52% |
| Variance | 0.014% | 0.012% |
| Annualized Variance | 3.51% | 3.02% |
| Semi-Variance | 0.0069% | 0.0062% |
| Annualized Semi-Variance | 1.75% | 1.57% |
| Standard Deviation | 1.18% | 1.09% |
| Annualized Standard Deviation | 18.73% | 17.38% |
| Semi-Deviation | 0.83% | 0.79% |
| Annualized Semi-Deviation | 13.22% | 12.52% |
| Mean Absolute Deviation | 0.76% | 0.69% |
| CVaR at 95% | 2.70% | 2.57% |
| EVaR at 95% | 5.42% | 5.51% |
| Worst Realization | 10.41% | 10.49% |
| CDaR at 95% | 14.09% | 13.21% |
| MAX Drawdown | 31.85% | 34.81% |
| Average Drawdown | 2.98% | 2.59% |
| EDaR at 95% | 19.42% | 20.91% |
| First Lower Partial Moment | 0.38% | 0.35% |
| Ulcer Index | 0.048 | 0.044 |
| Gini Mean Difference | 1.15% | 1.06% |
| Value at Risk at 95% | 1.66% | 1.55% |
| Drawdown at Risk at 95% | 10.33% | 9.47% |
| Entropic Risk Measure at 95% | 3.00 | 3.00 |
| Fourth Central Moment | 0.000038% | 0.000029% |
| Fourth Lower Partial Moment | 0.000014% | 0.000015% |
| Skew | 35.24% | -13.45% |
| Kurtosis | 1942.93% | 1988.50% |
| Sharpe Ratio | 0.061 | 0.056 |
| Annualized Sharpe Ratio | 0.97 | 0.89 |
| Sortino Ratio | 0.086 | 0.078 |
| Annualized Sortino Ratio | 1.37 | 1.24 |
| Mean Absolute Deviation Ratio | 0.095 | 0.089 |
| First Lower Partial Moment Ratio | 0.19 | 0.18 |
| Value at Risk Ratio at 95% | 0.043 | 0.040 |
| CVaR Ratio at 95% | 0.027 | 0.024 |
| Entropic Risk Measure Ratio at 95% | 0.00024 | 0.00021 |
| EVaR Ratio at 95% | 0.013 | 0.011 |
| Worst Realization Ratio | 0.0069 | 0.0059 |
| Drawdown at Risk Ratio at 95% | 0.0070 | 0.0065 |
| CDaR Ratio at 95% | 0.0051 | 0.0047 |
| Calmar Ratio | 0.0023 | 0.0018 |
| Average Drawdown Ratio | 0.024 | 0.024 |
| EDaR Ratio at 95% | 0.0037 | 0.0029 |
| Ulcer Index Ratio | 0.015 | 0.014 |
| Gini Mean Difference Ratio | 0.063 | 0.058 |
| Effective Number of Assets | 8.434345967826763 | 18.441257685468493 |
| Assets Number | 20 | 20 |